#!/bin/bash

set -e
[ "$DEBUG" ] && set -x

USAGE="USAGE: $(basename "$0") [run|edit] <docker_run_params>"
GOSS_FILES_PATH="${GOSS_FILES_PATH:-.}"

# Container runtime
CONTAINER_RUNTIME="${CONTAINER_RUNTIME:-docker}"

info() {
    echo -e "INFO: $*" >&2;
}
error() {
    echo -e "ERROR: $*" >&2;
    exit 1;
}

cleanup() {
    set +e
    # There is a chance that the log will not be written completely.
    # Exit the container and retrieve the last line of logs to ensure
    # that all logs have been read.
    info "Stopping container"
    $CONTAINER_RUNTIME stop --time 1 "$id" > /dev/null 2>&1
    $CONTAINER_RUNTIME logs --tail 1 "$id" > /dev/null 2>&1

    { kill "$log_pid" && wait "$log_pid"; } 2> /dev/null
    if [ -n "$CONTAINER_LOG_OUTPUT" ]; then
        info "Copying log"
        cp "$tmp_dir/docker_output.log" "$CONTAINER_LOG_OUTPUT"
    fi
    [ "$DEBUG" ] || rm -rf "$tmp_dir"
    if [[ $id ]]; then
        info "Deleting container"
        $CONTAINER_RUNTIME rm -vf "$id" > /dev/null
    fi
}

run(){
    # Copy in goss
    cp "${GOSS_PATH}" "$tmp_dir/goss"
    chmod 755 "$tmp_dir/goss"
    [[ -e "${GOSS_FILES_PATH}/${GOSS_FILE:-goss.yaml}" ]] && cp "${GOSS_FILES_PATH}/${GOSS_FILE:-goss.yaml}" "$tmp_dir/goss.yaml" && chmod 644 "$tmp_dir/goss.yaml"
    [[ -e "${GOSS_FILES_PATH}/goss_wait.yaml" ]] && cp "${GOSS_FILES_PATH}/goss_wait.yaml" "$tmp_dir" && chmod 644 "$tmp_dir/goss_wait.yaml"
    [[ -n "${GOSS_VARS}" ]] && [[ -e "${GOSS_FILES_PATH}/${GOSS_VARS}" ]] && cp "${GOSS_FILES_PATH}/${GOSS_VARS}" "$tmp_dir" && chmod 644 "$tmp_dir/${GOSS_VARS}"
    if [ -n "$GOSS_ADDITIONAL_COPY_PATH" ]; then
        for dir in "$(echo "$GOSS_ADDITIONAL_COPY_PATH" | sed 's/:/ /g')"; do
            cp -r ${dir} "${tmp_dir}/"
            chmod -R 755 "$tmp_dir/$(basename ${dir})"
        done
    fi

    # Switch between mount or cp files strategy
    GOSS_FILES_STRATEGY=${GOSS_FILES_STRATEGY:="mount"}
    case "$GOSS_FILES_STRATEGY" in
      mount)
        info "Starting $CONTAINER_RUNTIME container"
        if [ "$CONTAINER_RUNTIME" == "podman" -a $# == 2 ]; then
            id=$($CONTAINER_RUNTIME run -d -v "$tmp_dir:/goss:z" "${@:2}" sleep infinity)
        else
            id=$($CONTAINER_RUNTIME run -d -v "$tmp_dir:/goss:z" "${@:2}")
        fi
        ;;
      cp)
        info "Creating $CONTAINER_RUNTIME container"
        id=$($CONTAINER_RUNTIME create "${@:2}")
        info "Copy goss files into container"
        $CONTAINER_RUNTIME cp "$tmp_dir/." "$id:/goss"
        info "Starting $CONTAINER_RUNTIME container"
        $CONTAINER_RUNTIME start "$id" > /dev/null
        ;;
      *) error "Wrong goss files strategy used! Correct options are \"mount\" or \"cp\"."
    esac

    $CONTAINER_RUNTIME logs -f "$id" > "$tmp_dir/docker_output.log" 2>&1 &
    log_pid=$!
    info "Container ID: ${id:0:8}"
}

get_docker_file() {
    local cid=$1  # Docker container ID
    local src=$2  # Source file path (in the container)
    local dst=$3  # Destination file path

    if $CONTAINER_RUNTIME exec "${cid}" sh -c "test -e ${src}" > /dev/null; then
        mkdir -p "${GOSS_FILES_PATH}"
        $CONTAINER_RUNTIME cp "${cid}:${src}" "${dst}"
        info "Copied '${src}' from container to '${dst}'"
    fi
}

# Main
tmp_dir=$(mktemp -d ${DGOSS_TEMP_DIR:-/tmp}/tmp.XXXXXXXXXX)
chmod 777 "$tmp_dir"
trap 'ret=$?;cleanup;exit $ret' EXIT

GOSS_PATH="${GOSS_PATH:-$(which goss 2> /dev/null || true)}"
[[ $GOSS_PATH ]] || { error "Couldn't find goss installation, please set GOSS_PATH to it"; }
[[ ${GOSS_OPTS+x} ]] || GOSS_OPTS="--color --format documentation"
[[ ${GOSS_WAIT_OPTS+x} ]] || GOSS_WAIT_OPTS="-r 30s -s 1s > /dev/null"
GOSS_SLEEP=${GOSS_SLEEP:-0.2}

[[ $CONTAINER_RUNTIME =~ ^(docker|podman)$ ]] || { error "Runtime must be one of docker or podman"; }

case "$1" in
    run)
        run "$@"
        if [[ -e "${GOSS_FILES_PATH}/goss_wait.yaml" ]]; then
            info "Found goss_wait.yaml, waiting for it to pass before running tests"
            if [[ -z "${GOSS_VARS}" ]]; then
                if ! $CONTAINER_RUNTIME exec "$id" sh -c "/goss/goss -g /goss/goss_wait.yaml validate $GOSS_WAIT_OPTS"; then
                    $CONTAINER_RUNTIME logs "$id" >&2
                    error "goss_wait.yaml never passed"
                fi
            else
                if ! $CONTAINER_RUNTIME exec "$id" sh -c "/goss/goss -g /goss/goss_wait.yaml --vars='/goss/${GOSS_VARS}' validate $GOSS_WAIT_OPTS"; then
                    $CONTAINER_RUNTIME logs "$id" >&2
                    error "goss_wait.yaml never passed"
                fi
            fi
        fi
        [[ $GOSS_SLEEP ]] && { info "Sleeping for $GOSS_SLEEP"; sleep "$GOSS_SLEEP"; }
        info "Container health"
        if [ "true" != "$($CONTAINER_RUNTIME inspect -f '{{.State.Running}}' "$id")" ]; then
            $CONTAINER_RUNTIME logs "$id" >&2
            error "the container failed to start"
        fi
        info "Running Tests"
        if [[ -z "${GOSS_VARS}" ]]; then
            $CONTAINER_RUNTIME exec "$id" sh -c "/goss/goss -g /goss/goss.yaml validate $GOSS_OPTS"
        else
            $CONTAINER_RUNTIME exec "$id" sh -c "/goss/goss -g /goss/goss.yaml --vars='/goss/${GOSS_VARS}' validate $GOSS_OPTS"
        fi
        ;;
    edit)
        run "$@"
        info "Run goss add/autoadd to add resources"
        $CONTAINER_RUNTIME exec -it "$id" sh -c 'cd /goss; PATH="/goss:$PATH" exec sh'
        get_docker_file "$id" "/goss/goss.yaml" "${GOSS_FILES_PATH}/${GOSS_FILE:-goss.yaml}"
        get_docker_file "$id" "/goss/goss_wait.yaml" "${GOSS_FILES_PATH}/goss_wait.yaml"
        if [[ -n "${GOSS_VARS}" ]]; then
            get_docker_file "$id" "/goss/${GOSS_VARS}" "${GOSS_FILES_PATH}/${GOSS_VARS}"
        fi
        ;;
    *)
        error "$USAGE"
esac
